package mqtt

import (
	"encoding/json"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"github.com/spf13/cast"
	"longmen/server/config/global"
	cricketSrv "longmen/server/internal/services/nami/services/sports/cricket"
	"longmen/server/internal/services/nami/services/sports/cricket/crcache"
	"longmen/server/pkg/db/mysql/models/models_sports"
)

const cricketTopicName = "sports/cricket/match.v1"

// refer:	https://www.nami.com/docs?id=2005
type (
	CricketMQ        struct{}
	CricketScores    []any
	CricketTimelines []any
	CricketPlayers   []any
	CricketResponse  struct {
		ID       int              `json:"id"`
		Score    CricketScores    `json:"score"`
		Timeline CricketTimelines `json:"timeline"`
		Players  CricketPlayers   `json:"players"`
	}
)

func (f *CricketMQ) InitTopic(client mqtt.Client) {
	token := client.Subscribe(cricketTopicName, 1, f.MessageHandler)
	token.Wait()
	global.Log.Info("init topic 板球: ", cricketTopicName)
}

func (f *CricketMQ) MessageHandler(client mqtt.Client, msg mqtt.Message) {
	global.Log.Debugf("[板球] receive mqtt msg")
	err := f.Exec(msg.Payload(), msg.MessageID())
	if err != nil {
		msg.Retained()
		return
	}
	msg.Ack()
}

func (f *CricketMQ) Exec(body []byte, MessageID uint16) error {
	//global.Log.Info("cricket receive: ", string(body))
	records := make([]CricketResponse, 0)
	err := json.Unmarshal(body, &records)
	if err != nil {
		global.Log.Error("反序列化板球数据失败：", err, string(body))
		return err
	}

	if len(records) > 0 {
		if len(records[0].Score) > 0 {
			err = f.publishScores(records)
			if err != nil {
				return err
			}
		}

		if len(records[0].Players) > 0 {
			err = f.publishPlayers(records)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// publishScores 实时比分更新
func (f *CricketMQ) publishScores(records []CricketResponse) error {
	updateInterfaces := make([]cricketSrv.UpdateMatchInterface, 0)

	// extract scores as slice,
	pubs := make([]crcache.PublishScoreInterface, 0)
	for _, record := range records {
		updateInterfaces = append(updateInterfaces, record.Score)
		pubs = append(pubs, record.Score)
	}

	// update db,
	if len(updateInterfaces) > 0 {
		cricketSrv.UpdateMatchScore(updateInterfaces)
	}

	// update/push cache (redis / es / tencentIM ?),
	if len(pubs) > 0 {
		err := crcache.NewScoreCache().PublishLiveScore(pubs)
		if err != nil {
			global.Log.Error("推送nat数据失败", err)
			return err
		}
	}
	return nil
}

// publishPlayers 实时阵容推送
func (f *CricketMQ) publishPlayers(records []CricketResponse) error {
	// TODO: 暂时 不需要
	return nil
}

func (f *CricketMQ) Quit(client mqtt.Client) {
	client.Unsubscribe(cricketTopicName)
}

func (b CricketScores) GetScore() crcache.Score {
	if len(b) < 5 {
		global.Log.Errorf("板球 推送 score 无效 (len = %v)", len(b))
		return crcache.Score{}
	}

	result := crcache.Score{
		MatchId:     int(b[0].(float64)),
		MatchStatus: int(b[1].(float64)),
	}

	rawScores, okScore := b[3].(map[string]any)
	if !okScore {
		return result
	}

	// get ft,
	rawScoresFtAny, ok := rawScores["ft"]
	if ok {
		rawScoresFt, ok := rawScoresFtAny.([]any)
		if ok && len(rawScoresFt) >= 2 {
			result.HomeTotalScore = cast.ToInt(rawScoresFt[0])
			result.AwayTotalScore = cast.ToInt(rawScoresFt[1])
		}
	}

	// get p* and round,
	var rawScoresPxAny any
	var rawScoresPx []any
	var round int

	if rawScoresPxAny, ok = rawScores["p1"]; ok { // p1
		round = 1
	} else if rawScoresPxAny, ok = rawScores["p1"]; ok { // p2
		round = 2
	}

	if ok {
		rawScoresPx, ok = rawScoresPxAny.([]any) // to slice,
		if ok && len(rawScoresPx) >= 2 {
			result.Round = round
			result.HomeScore = cast.ToInt(rawScoresPx[0])
			result.AwayScore = cast.ToInt(rawScoresPx[1])
		}
	}

	return result
}

// update match 表时 调用, 获取 update 字段的 值.
func (b CricketScores) GetUpdate() models_sports.CricketMatch {
	if len(b) < 5 {
		return models_sports.CricketMatch{}
	}
	id := int(b[0].(float64))
	status := models_sports.CricketMatchStatus(b[1].(float64))
	scoresBs, _ := json.Marshal(b[3])
	extraScoresBs, _ := json.Marshal(b[4])
	return models_sports.CricketMatch{
		ID:          id,
		StatusID:    status,
		Scores:      string(scoresBs),
		ExtraScores: string(extraScoresBs),
	}
}
